<?php

class Wlib_Acl_AclMvcManager
{
    /**
     * @var Zend_Acl
     */
    protected $_acl = null;

    protected $_userRoles = null;

    protected $_authIdentity = null;

    protected $_isRegisterActionHelper = true;

    protected $_aclRights = null;

    public function __construct($options = null)
    {
        if (is_array($options)) {
            $this->setOptions($options);
        } elseif ($options instanceof Zend_Config) {
            $this->setConfig($options);
        }

        if($this->getIsRegisterActionHelper()) {
            $this->registerActionHelper();
        }
    }

    /**
     * Устанавливает опции из входящего массива
     *
     * @param array $options
     * @return Wlib_Acl_AclMvcManager
     */

    public function setOptions(array $options)
    {
        foreach ($options as $key => $value) {
            $method = 'set' . ucfirst($key);
            if (method_exists($this, $method)) {
                $this->$method($value);
            }
        }

        return $this;
    }
    
    /**
     * Устанавливает опции из входящего объекта
     *
     * @param  Zend_Config $config
     * @return Wlib_Acl_AclMvcManager
     */
    public function setConfig(Zend_Config $config)
    {
        return $this->setOptions($config->toArray());
    }

    /**
     * Проверяет, есть ли доступ у текущего пользователя к определенному действию (action) контроллера,
     * заданному параметрами (module, controller, action) в $routeParams
     *
     * Если какой-то из входных параметров (module, controller, action) опущен, то он заменяется соответственно
     * (default, index, index) 
     *
     * Сущность, характеризующая пользователя, по умолчанию извлекается из сессии. Ее можно переопределить
     * в методе setAuthIdentity
     *
     * <code>
     * <?php
     * $aclMvcManager->isControllerActionAllowed(array(
     *     'action' => 'add', 'controller' => 'posts', 'module' => 'blog' 
     * ));
     * ?>
     * </code>
     * @param array $routeParams
     * @return bool
     */
    public function isControllerActionAllowed($routeParams)
    {
        $authIdentity = $this->getAuthIdentity();

        /**
         * Если это супер пользователь, то ему разрешено все
         */
        if($authIdentity && $authIdentity->id == 1) {
            return true;
        }

        $routeParams['module'] = isset($routeParams['module']) ? $routeParams['module'] : 'default';
        $routeParams['controller'] = isset($routeParams['controller']) ? $routeParams['controller'] : 'index';
        $routeParams['action'] = isset($routeParams['action']) ? $routeParams['action'] : 'index';

        $roleId = $this->getAclRoleIdByAuthIdentity($authIdentity);
        $resource = 'mvc:' . $routeParams['module'] . '.' . $routeParams['controller'];
        $privelege = $routeParams['action'];

        $acl = $this->getAcl();
        if(!$acl->has($resource)) {
            $acl->addResource($resource);
        }

        $isAclAllowed = $acl->isAllowed($roleId, $resource, $privelege);
        return $isAclAllowed;
    }

    public function getAclRoleIdByAuthIdentity($authIdentity)
    {
        $roleId = 'guest';
        if($authIdentity && $authIdentity->role) {
            $userRoles = $this->getUserRoles();

            if(isset($userRoles[$authIdentity->role])) {
                $roleId = $userRoles[$authIdentity->role];
            }
        }

        return $roleId;
    }

    /**
     * @return Zend_Acl
     */
    public function getAcl()
    {
        if(null === $this->_acl) {
            $acl = $this->_getDefaultAcl();

            foreach($this->getAclRights() as $moduleName => $moduleRights) {
                foreach($moduleRights as $controllerName => $controllerRights) {
                    $resource = 'mvc:' . $moduleName . '.' . $controllerName;
                    if(!$acl->has($resource)) {
                        $acl->addResource($resource);
                    }
                    foreach($controllerRights as $actionName => $allowRolesString) {
                        $allowRolesList = explode(' ', $allowRolesString);
                        foreach($allowRolesList as $role) {
                            if(empty($role)) {
                                continue;
                            }
                            
                            if(!$acl->hasRole($role)) {
                                $acl->addRole($role);
                            }

                            $acl->allow($role, $resource, $actionName);
                        }
                    }
                }
            }

            $this->_acl = $acl;
        }

        return $this->_acl;
    }

    /**
     * @return Zend_Acl
     */
    public function _getDefaultAcl()
    {
        $acl = new Zend_Acl();

        $acl->addRole('guest');

        $acl->addResource('mvc:default.error');
        $acl->addResource('mvc:default.auth');

        $acl->allow('guest', 'mvc:default.error', 'error');
        $acl->allow('guest', 'mvc:default.auth', 'index');
        $acl->allow('guest', 'mvc:default.auth', 'logout');

        foreach($this->getUserRoles() as $role) {
            $acl->addRole($role, 'guest');
        }        

        return $acl;
    }

    public function registerActionHelper()
    {
        $aclMvcHelper = new Wlib_Controller_Action_Helper_AclMvc();
        $aclMvcHelper->setAclMvcManager($this);
        Zend_Controller_Action_HelperBroker::addHelper($aclMvcHelper);
    }

    public function setAcl($acl)
    {
        $this->_acl = $acl;
    }

    public function getUserRoles()
    {
        return $this->_userRoles;
    }

    public function setUserRoles($userRoles)
    {
        $this->_userRoles = $userRoles;
        return $this;
    }

    public function getAuthIdentity()
    {
        return $this->_authIdentity;
    }

    public function setAuthIdentity($authIdentity)
    {
        $this->_authIdentity = $authIdentity;
        return $this;
    }

    public function getIsRegisterActionHelper()
    {
        return $this->_isRegisterActionHelper;
    }

    public function setIsRegisterActionHelper($isRegisterActionHelper)
    {
        $this->_isRegisterActionHelper = $isRegisterActionHelper;
    }

    public function getAclRights()
    {
        return $this->_aclRights;
    }

    public function setAclRights($aclRights)
    {
        $this->_aclRights = $aclRights;
        return $this;
    }
}